#import('dart:dom');
#source('collections.dart');

/**
 * Exercises the collections in various ways. Unfortunately, these are not real
 * tests as thy don't verify the behaviour.
 */
void main() {
  try {
    // Iterables
    testReadList();
    testWriteList();
    testReadSet();
    testWriteSet();
    testWriteHashSet();

    // Maps
    testReadMap();
    testWriteMap();
    testWriteHashMap();
    testWriteLinkedHashMap();

    title("SUCCESS!");
  } catch (Exception e) {
    title("FAILURE: " + e.toString());
  }
}

// Tests

void testReadList() {
  title("testReadList");
  var rl = new ReadList.from([1, 2, 3, 4, 3]);
  // rl.add(5); // Compiler warning
  exerciseReadList(rl);
}

void testWriteList() {
  title("testWriteList");
  
  var wl = new WriteList.from([1, 2, 3, 4]);
  exerciseReadList(wl); // Note assignment to ReadList
  wl[1] = 333;
  wl.add(5);
  wl.addLast(55);
  wl.addAll([7, 8, 9]);
  wl.sort((int a, int b) {
    return b - a; // descending
  });
  wl.removeLast();
  wl.length = 25;
  printCol("wl", wl);
  WriteList<int> sub = wl.getRange(1, 3); // Note assignment to WriteList
  printCol("wl.getRange(1, 3)", sub);
  sub.add(10);
  printCol("modified range", sub);
  wl.setRange(2, 3, new ReadList.from([11, 12, 13, 14, 15]), 1);
  printCol("wl.setRange(...)", wl);
  wl.removeRange(2, 3);
  printCol("wl.removeRange(...)", wl);
  wl.insertRange(2, 3, 16);
  printCol("wl.insertRange(...)", wl);
  wl.clear();
  printCol("wl.clear()", wl);
}

void testReadSet() {
  title("testReadSet");
  var rs = new ReadSet.from([1, 2, 3, 4]);
  // rs.add(5); // Compiler warning
  exerciseReadSet(rs);
}

void testWriteSet() {
  title("testWriteSet");
  var ws = new WriteSet();
  ws = new WriteSet.from([1, 2, 3, 4]);
  exerciseWriteSet(ws);
}

void testWriteHashSet() {
  title("testWriteHashSet");
  var ws = new WriteHashSet();
  ws = new WriteHashSet.from([1, 2, 3, 4]);
  exerciseWriteSet(ws); // Note assignment to WriteSet
}

ReadMap<int, String> createTestMap() {
  WriteMap<int, String> data = new WriteMap<int, String>();
  data[1] = "a";
  data[2] = "b";
  data[3] = "c";
  data[5] = "d";
  data[7] = "e";
  return data;
}

void testReadMap() {
  title("testReadMap");
  
  ReadMap<int, String> rm = new ReadMap.from(createTestMap());
  // rm[3] = "c"; // Should be compiler warning but isn't, gotta file a bug?
  exerciseReadMap(rm);
}

void testWriteMap() {
  title("testWriteMap");
  
  WriteMap<int, String> wm = new WriteMap.from(createTestMap());
  exerciseWriteMap(wm);
}

void testWriteHashMap() {
  title("testWriteHashMap");
  
  WriteHashMap<int, String> wm = new WriteHashMap.from(createTestMap());
  exerciseWriteMap(wm); // Note assignment to WriteMap
}

void testWriteLinkedHashMap() {
  title("testWriteLinkedHashMap");

  WriteLinkedHashMap<int, String> wm =
    new WriteLinkedHashMap.from(createTestMap());
  exerciseWriteMap(wm); // Note assignment to WriteMap
}

bool f(int e) {
  return e > 2;
}

void exerciseCollection(Collection<int> c) {
  printCol("c", c);
  
  StringBuffer sb = new StringBuffer("[");
  Iterator<int> i = c.iterator();
  while (i.hasNext()) {
    var a = i.next();
    sb.add(a);
  }
  sb.add("]");
  write("Iterator -> " + sb);
  
  var count = 0;
  c.forEach((int) {
    count++;
  });
  write("Count forEach = " + count);
  printCol("c.filter(e > 2)", c.filter(f));
  write("c.every(e > 2) = " + c.every(f));
  write("c.some(e > 2) = " + c.some(f));
  write("c.isEmpty() = " + c.isEmpty());
  write("c.length = " + c.length);
}

void exerciseReadList(ReadList<int> l) {
  exerciseCollection(l); // Note assignment to Collection
  write("l[3] = " + l[3]);
  write("l.indexOf(4) = " + l.indexOf(4));
  write("l.lastIndexOf(3) = " + l.lastIndexOf(3));
  write("l.last() = " + l.last());
  ReadList<int> sub = l.getRange(1, 3); // Note assignment to ReadList
  printCol("l.getRange(1, 3)", sub);
}

void exerciseReadSet(ReadSet<int> s) {
  exerciseCollection(s);
  write("s.contains(2) = " + s.contains(2));
  write("s.isSubsetOf([1, 2, 3, 4, 5, 6, 7, 8]) = " +
    s.isSubsetOf([1, 2, 3, 4, 5, 6, 7, 8]));
  write("s.containsAll([2, 4]) = " + s.containsAll([2, 4]));
  write("s.intersection([3, 4, 5, 6]) = " + s.intersection([3, 4, 5, 6]));
}

void exerciseWriteSet(WriteSet<int> ws) {
  ws.addAll([5, 6, 7, 8]);
  exerciseReadSet(ws); // Note assignment to ReadSet
  ws.remove(6);
  ws.add(20);
  ws.removeAll([5, 8]);
  printCol("ws", ws);
  ws.clear();
  printCol("ws.clear()", ws);
}

void exerciseReadMap(ReadMap<int, String> rm) {
  write("rm.containsValue('c') = " + rm.containsValue('c'));
  write("rm.containsKey(100) = " + rm.containsKey(100));
  rm.forEach((int k, String v) {
    write("rm[" + k + "] = " + v);
  });
  printCol("rm.getKeys()", rm.getKeys());
  printCol("rm.getValues()", rm.getValues());
  write("rm.length = " + rm.length);
  write("rm.isEmpty() = " + rm.isEmpty());
}

void exerciseWriteMap(WriteMap<int, String> wm) {
  exerciseReadMap(wm); // Note assignment to ReadMap
  wm[1] = "x";
  wm[40] = "y";
  wm.putIfAbsent(50, () {
    return "z";
  });
  wm.remove(5);
  wm.clear();
}

// Common methods

void write(String str) {
  HTMLDocument doc = window.document;
  HTMLParagraphElement p = doc.createElement('p');
  p.textContent = str;
  doc.body.appendChild(p);
}

void title(String titleString) {
  HTMLDocument doc = window.document;
  HTMLParagraphElement h2 = doc.createElement('h2');
  h2.textContent = titleString;
  doc.body.appendChild(h2);
}

void printCol(String title, Collection<Object> c) {
  var sb = new StringBuffer(title);
  sb.add(" = [");
  bool first = true;
  for (int i in c) {
    if (!first) {
      sb.add(",");
    }
    if (i === null) {
      sb.add("null");
    } else {
      sb.add(i);
    }
    first = false;
  }
  sb.add("]");
  write(sb.toString());
}
